using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

using EntModApp.EntServices;
using EntModApp.EntServices.Data;

namespace DemoApp.DataAccess 
{
  public partial class SubjectQueryableRepository : 
    DbContextQueryableRepository<DemoAppModelDbContext, Subject>,
    IQueryableSubjectRepository
  {
    public SubjectQueryableRepository(DemoAppModelDbContext context)
      : this(context, false)
    {
    }

    public SubjectQueryableRepository()
      : this(new DemoAppModelDbContext(), true)
    {
    }

    protected SubjectQueryableRepository(DemoAppModelDbContext context, bool ownContext)
      : base(context, context.Subjects, ownContext)
    {
    }
  }

  public partial class SubjectDataRepository : 
    DbContextDataRepository<DemoAppModelDbContext, Subject, SubjectLoadSettings>,
    ISubjectRepository
  {
    public SubjectDataRepository(DemoAppModelDbContext context)
      : this(context, false)
    {
    }

    public SubjectDataRepository()
      : this(new DemoAppModelDbContext(), true)
    {
    }

    protected SubjectDataRepository(DemoAppModelDbContext context, bool ownContext)
      : base(context, context.Subjects, ownContext)
    {
    }

    public override IEnumerable<Subject> RetrieveAll(out int total, bool retrieveTotal)
    {
      var query = new SubjectLoadSettings
      {
        Paging = new PagingSettings { ComputeTotal = retrieveTotal }
      };
      return Retrieve(query, out total);
    }

    public override IEnumerable<Subject> Retrieve(SubjectLoadSettings query, out int total)
    {
      var dbQuery = DataSet.ApplySubjectLoadSettings(query, out total);
      return dbQuery.ToList();
    }

    public override int Count()
    {
      return DataSet.Count();
    }

    public override int Count(SubjectLoadSettings query)
    {
      var dbQuery = DataSet.ApplySubjectFilterExpression(query.Filter);
      return dbQuery.Count();
    }
  }

  public static class SubjectQueryableRepositoryExtensions
  {
    public static IQueryable<Subject> ApplySubjectLoadSettings(this IQueryable<Subject> query, SubjectLoadSettings loadSettings, out int total)
    {
      if (loadSettings == null)
        loadSettings = new SubjectLoadSettings();

      query = query.ApplySubjectInclusionSettings(loadSettings.Inclusion);

      query = query.ApplySubjectFilterExpression(loadSettings.Filter);

      query = query.ApplySubjectSortingSettings(loadSettings.Sorting);

      query = query.ApplyPagingSettings(loadSettings.Paging, out total);

      query = query.ApplySubjectSelectionSettings(loadSettings.Selection);

      return query;
    }

    public static IEnumerable<string> GetDirectSubjectInclusionPaths(this SubjectInclusionSettings inclusionSettings)
    {
      if (inclusionSettings.IncludeCreator) yield return "Creator";
      if (inclusionSettings.IncludeEditors) yield return "Editors";
      if (inclusionSettings.IncludeParentSubject) yield return "ParentSubject";
      if (inclusionSettings.IncludeSubSubjects) yield return "SubSubjects";

      yield break;
    }

    public static IEnumerable<string> GetSubjectInclusionPaths(this SubjectInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetDirectSubjectInclusionPaths())
        yield return path;

      if (inclusionSettings.IncludeCreator)
        foreach (var referencePath in inclusionSettings.IncludedCreatorSettings.Inclusion.GetUserInclusionPaths())
          yield return "Creator." + referencePath;
      if (inclusionSettings.IncludeEditors)
        foreach (var referencePath in inclusionSettings.IncludedEditorsSettings.Inclusion.GetUserInclusionPaths())
          yield return "Editors." + referencePath;
      if (inclusionSettings.IncludeParentSubject)
        foreach (var referencePath in inclusionSettings.IncludedParentSubjectSettings.Inclusion.GetSubjectInclusionPaths())
          yield return "ParentSubject." + referencePath;
      if (inclusionSettings.IncludeSubSubjects)
        foreach (var referencePath in inclusionSettings.IncludedSubSubjectsSettings.Inclusion.GetSubjectInclusionPaths())
          yield return "SubSubjects." + referencePath;

      yield break;
    }

    public static IQueryable<Subject> ApplySubjectInclusionSettings(this IQueryable<Subject> query, SubjectInclusionSettings inclusionSettings)
    {
      foreach (var path in inclusionSettings.GetSubjectInclusionPaths())
        query = query.Include(path);

      return query;
    }

    public static IQueryable<Subject> ApplySubjectFilterExpression(this IQueryable<Subject> query, SubjectFilterExpression filterExpression)
    {
      if (filterExpression.ByKey != null)
        query = query.Where(o => o.SubjectId == filterExpression.ByKey.SubjectId);

      var parameter = Expression.Parameter(typeof(Subject), "e");
      if (filterExpression.SubjectIdFilter != null)
        query = query.Where(filterExpression.SubjectIdFilter.CreateExpressionBody<Subject, Guid>(SubjectPropertyNames.SubjectId, parameter).ToLambda<Subject>(parameter));
      if (filterExpression.UrlFilter != null)
        query = query.Where(filterExpression.UrlFilter.CreateExpressionBody<Subject, string>(SubjectPropertyNames.Url, parameter).ToLambda<Subject>(parameter));
      if (filterExpression.TitleFilter != null)
        query = query.Where(filterExpression.TitleFilter.CreateExpressionBody<Subject, string>(SubjectPropertyNames.Title, parameter).ToLambda<Subject>(parameter));
      if (filterExpression.DescriptionFilter != null)
        query = query.Where(filterExpression.DescriptionFilter.CreateExpressionBody<Subject, string>(SubjectPropertyNames.Description, parameter).ToLambda<Subject>(parameter));
      if (filterExpression.ImageUrlFilter != null)
        query = query.Where(filterExpression.ImageUrlFilter.CreateExpressionBody<Subject, string>(SubjectPropertyNames.ImageUrl, parameter).ToLambda<Subject>(parameter));
      if (filterExpression.CreateDateFilter != null)
        query = query.Where(filterExpression.CreateDateFilter.CreateExpressionBody<Subject, DateTime>(SubjectPropertyNames.CreateDate, parameter).ToLambda<Subject>(parameter));
      if (filterExpression.ModifyDateFilter != null)
        query = query.Where(filterExpression.ModifyDateFilter.CreateExpressionBody<Subject, DateTime>(SubjectPropertyNames.ModifyDate, parameter).ToLambda<Subject>(parameter));
      if (filterExpression.IsDisabledFilter != null)
        query = query.Where(filterExpression.IsDisabledFilter.CreateExpressionBody<Subject, bool>(SubjectPropertyNames.IsDisabled, parameter).ToLambda<Subject>(parameter));

      if (filterExpression.Skip.HasValue)
        query = query.Skip(filterExpression.Skip.Value);
      if (filterExpression.Take.HasValue)
        query = query.Take(filterExpression.Take.Value);

      return query;
    }

    public static IQueryable<Subject> ApplySubjectSortingSettings(this IQueryable<Subject> query, SubjectSortingSettings sortingSettings)
    {
      return query;
    }

    public static IQueryable<Subject> ApplySubjectSelectionSettings(this IQueryable<Subject> query, SubjectSelectionSettings selectionSettings)
    {
      return query;
    }

  }
}
